perf(treemap): on-demand subtree fetch + visible directory labels#120
Merged
Conversation
Two related improvements to the dashboard treemap, building on the depth-8 cap shipped in v0.2.1: **Phase 2 — backend path-rooted subtree.** Adds an optional `path` query parameter to `GET /api/file-tree` so the frontend can fetch just a focused subtree instead of the whole tree. The Cypher query filters by `STARTS WITH path/`, the tree builder strips the prefix during walk so the local root represents the focused directory, and `@Cacheable` is keyed on path so different anchors are cached separately. Path inputs are normalized + validated in the controller (no `..`, no leading slash, ≤1024 chars) before reaching the query layer. **Phase 3 — frontend on-demand drill.** When the user clicks a directory rendered as a depth-cap leaf (type=directory, no children, nodeCount > 0), Dashboard fetches its subtree via the new path endpoint, splices the response into the in-memory tree at that node, and drills into it in the same gesture. A small loading indicator appears in the breadcrumb during the fetch. Concurrent clicks on the same path dedupe via a ref-tracked in-flight set; failures clear the flag so retries work. **UX fix — labels on directory cells.** The design-system Treemap canvas only paints names on `isLeaf` cells, so directories at the visible drill level rendered as unlabelled rectangles (names only appeared on hover). Dashboard now feeds the treemap a render-only flat copy of the focused level — directories are stripped of their children and given a pre-aggregated value so the canvas treats them as leaves and paints the label, while the original TreemapNode (with intact children) stays in `focusedRoot` and is recovered via a `renderToOriginal` WeakMap in the click handler so drill-down keeps working. Test plan: - mvn test (3706 / 3706 pass; 32 expected skips) - npm run build (197.47 kB / 63.02 kB gzip — +0.5 kB vs main) - 4 new GraphControllerTest cases covering path passthrough, trailing-slash normalization, `..` traversal rejection, overlong path rejection - Manual: deploy + verify treemap shows directory names by default, drill into a depth-8 truncated directory triggers a fetch and shows children Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related improvements to the dashboard treemap, building on the depth-8 cap shipped in v0.2.1:
Phase 2 — backend path-rooted subtree
Adds an optional `path` query parameter to `GET /api/file-tree` so the frontend can fetch a focused subtree instead of the whole tree. Cypher filters via `STARTS WITH path/`, the tree builder strips the prefix during walk so the local root represents the focused directory, and `@Cacheable` is keyed on path so different anchors are cached separately. Path inputs are normalized + validated in the controller (no `..`, no leading slash, ≤1024 chars).
Phase 3 — frontend on-demand drill
When the user clicks a directory rendered as a depth-cap leaf (type=directory, no children, nodeCount > 0), Dashboard fetches its subtree via the new path endpoint, splices the response into the in-memory tree at that node, and drills into it in the same gesture. A small loading indicator appears in the breadcrumb during the fetch. Concurrent clicks dedupe via a ref-tracked in-flight set; failures clear the flag so retries work.
UX fix — labels on directory cells
The design-system Treemap canvas only paints names on `isLeaf` cells (line 114 of its canvas paint), so directories at the visible drill level showed as unlabelled rectangles — names only appeared on hover. Dashboard now feeds the treemap a render-only flat copy of the focused level: directories are stripped of their children and given a pre-aggregated value so the canvas treats them as leaves and paints the label. The original TreemapNode (with intact children) stays in `focusedRoot` and is recovered via a `renderToOriginal` WeakMap in the click handler so drill-down keeps working.
Test plan
🤖 Generated with Claude Code